package org.bbelovic.devel.snake;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;

/**
 * This class represents snake in Snake game. It wraps {@link Circle} instances that form snake's
 * body and contains methods that handle snake's move in given {@link Direction}.
 *
 */
public class Snake {

    /** default size of circles that are part snake's body */
    private static final int SNAKE_CIRCLE_SIZE = 20;
    /** initial snake's length */
    private static final int SNAKE_LENGTH = 3;
    /** collection holding all {@link Circle}s that are part of snake's body */
    private final List<Circle> circles = new ArrayList<Circle>();
    /** x-coordinate of snake's head*/
    private int x;
    /** y-coordinate of snake's head*/
    private int y;
    /** default direction for snake's movement */
    private Direction direction = Direction.RIGHT;

    /**
     * Constructs snake with default length of three circles.
     * @param x X coordinate of snake's head
     * @param y Y coordinate of snake's head
     */
    public Snake(final int x, final int y) {
        this.x = x;
        this.y = y;
        constructSnake();
    }

    /**
     * Draws snake on panel using panel's {@link Graphics}.
     * @param g Swing {@link Graphics} instance
     */
    public void drawSnake(Graphics g) {
        for (final Circle eachCircle: circles) {
            eachCircle.paint(g);
        }
    }

    /**
     * Moves snake in specified {@link Direction}. Direction is specified by {@link #setDirection(Direction)},
     * default direction is RIGHT.
     */
    public void moveInDirection() {
        this.x = this.x + direction.getX() * SNAKE_CIRCLE_SIZE;
        this.y = this.y + direction.getY() * SNAKE_CIRCLE_SIZE;
        doSnakeMotion(x, y);
    }

    /**
     * Enlarges snakes body by one circle.
     */
    public void growSnake(Circle newCircle) {
        final int lastX = circles.get(circles.size()-1).getX();
        final int lastY = circles.get(circles.size()-1).getY();
        newCircle = new Circle(Color.RED, SNAKE_CIRCLE_SIZE, lastX, lastY);
        circles.add(newCircle);
        
    }

    /**
     * Returns x coordinate for snake's head. Snake's head is a first snake's circle.
     * @return x coordinate of snake's head
     */
    public int getX() {
        return this.x;
    }

    /**
     * Returns y coordinate for snake's head. Snake's head is a first snake's circle.
     * @return y coordinate of snake's head
     */
    public int getY() {
        return this.y;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < circles.size(); i++) {
            sb.append(" "+ (i+1) + ". ");
            sb.append(circles.get(i).toString());
        }
        return sb.toString();
    }

    /**
     * Sets direction for snake's movement
     * @param direction {@link Direction} in which snake will be moving
     */
    public void setDirection(final Direction direction) {
        this.direction = direction;
    }

    /**
     * Returns direction in which is snake actually moving
     * @return {@link Direction} which is used for snake's movement
     */
    public Direction getDirection() {
        return this.direction;
    }

    /**
     * Returns snake's body size. Body size means size of one snake's circle.
     * @return size of one snake's circle
     */
    public static int getSnakesCircleSize() {
        return SNAKE_CIRCLE_SIZE;
    }

    /**
     * Constructs snake's body.
     */
    private void constructSnake() {
        for (int i = 0; i < SNAKE_LENGTH; i++) {
            final Circle circle = new Circle(Color.RED, SNAKE_CIRCLE_SIZE);
            circle.setX(x - i*SNAKE_CIRCLE_SIZE);
            circle.setY(y);
            circles.add(circle);
        }
    }

    /**
     * Moves snake to new coordinates
     * @param x new X coordinate for snake's head
     * @param y new Y coordinate for snake's head
     */
    private void doSnakeMotion(final int x, final int y) {
        int newX = x;
        int newY = y;
        int oldX = 0;
        int oldY = 0;
        for (int i = 0; i < circles.size(); i++) {
            final Circle actual = circles.get(i);
            oldX = actual.getX();
            oldY = actual.getY();
            actual.setX(newX);
            actual.setY(newY);
            newX = oldX;
            newY = oldY;
        }
        
    }
}
